//go:build linux
// +build linux

package main

import (
	// "errors"

	"encoding/base64"
	"io"
	"os"

	// syscall "golang.org/x/sys/unix"

	log "github.com/sirupsen/logrus"
)

type dict = map[string]any
type Fuse struct {
	Fds map[int]*os.File
}

func (fuser *Fuse) Init() *Fuse {
	fuser.Fds = make(map[int]*os.File)
	return fuser
}

func OSError(errno int, strerror string, filename string) dict {
	log.Printf("%v (%v) %v ", filename, errno, strerror)
	return dict{
		"errno":    errno,
		"strerror": strerror,
		"filename": filename,
	}
}

func FileExists(filename string) bool {
	_, err := os.Stat(filename)
	if err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

func Fd2File(fd int) *os.File {
	file := os.NewFile(uintptr(fd), "")
	return file
}

func Full_path(partial string) (path string) {
	// if partial.startswith("/~"):
	// 	partial = partial[1:]
	// 	partial = os.path.expanduser(partial)
	// 	return partial
	// elif partial.startswith("~"):
	// 	partial = os.path.expanduser(partial)
	// 	return partial
	// if partial.startswith("/"):
	// 	partial = partial[1:]
	// path = os.path.join(ROOT, partial)
	path = partial
	return path
}

// func (fuser *Fuse) Access(path string, mode int) bool {
// 	// fuse.access()
// 	full_path := Full_path(path)
// 	// log.Printf("%v %v", full_path, mode)
// 	// fp,err := os.OpenFile(full_path, mode, 0644)
// 	// defer fp.Close()
// 	fi, err := os.Stat(full_path)
// 	if err != nil {
// 		log.Printf("%v %v", full_path, err)
// 		return false
// 	}
// 	if int(fi.Mode())&mode == mode {
// 		// log.Printf("获得权限 %v", full_path)
// 		return true
// 	}
// 	// log.Printf("失败 %v", full_path)
// 	return false
// }

// func (fuser *Fuse) Chmod(path string, mode int) interface{} {
// 	//  fuse.chmod()
// 	full_path := Full_path(path)
// 	err := os.Chmod(full_path, os.FileMode(mode))
// 	// log.Printf("%v %v %v", full_path, mode, err)
// 	if err != nil {
// 		// log.Printf("%v %v", full_path, err)
// 		// return false
// 		// e, _ := err.(*os.PathError)
// 		// syscallErr, _ := errors.Unwrap(err).(*os.SyscallError)
// 		return OSError(1, err.Error(), path)
// 	}
// 	return true
// }

// func (fuser *Fuse) Chown(path string, uid int, gid int) bool {
// 	//  fuse.chown()
// 	full_path := Full_path(path)
// 	err := os.Chown(full_path, uid, gid)
// 	// log.Printf("%v %v %v %v", full_path, uid, gid, err)
// 	if err != nil {
// 		return false
// 	}
// 	return true
// }

// func (fuser *Fuse) Getattr(path string, fd int) dict {
// 	//  fuse.getattr()
// 	full_path := Full_path(path)
// 	var sys syscall.Stat_t
// 	err := syscall.Stat(full_path, &sys)
// 	if err != nil {
// 		if os.IsNotExist(err) {
// 			return OSError(2, "No such file or directory", path)
// 		}
// 		return OSError(1, err.Error(), path)
// 	}
// 	return dict{
// 		"st_mode":  sys.Mode,
// 		"st_ino":   sys.Ino,
// 		"st_dev":   sys.Dev,
// 		"st_nlink": sys.Nlink,
// 		"st_uid":   sys.Uid,
// 		"st_gid":   sys.Gid,
// 		"st_size":  sys.Size,
// 		"st_atime": sys.Atim.Sec,
// 		"st_mtime": sys.Mtim.Sec,
// 		"st_ctime": sys.Ctim.Sec,
// 	}
// }

// func (fuser *Fuse) Readdir(path string, fd int) interface{} {
// 	//  fuse.readdir()
// 	full_path := Full_path(path)
// 	files, err := os.ReadDir(full_path)
// 	if err != nil {
// 		if os.IsNotExist(err) {
// 			return OSError(2, "No such file or directory", path)
// 		}
// 		return OSError(1, err.Error(), path)
// 	}
// 	var outs []string
// 	outs = append(outs, ".")
// 	outs = append(outs, "..")
// 	for _, file := range files {
// 		outs = append(outs, file.Name())
// 	}
// 	return outs
// }

// func (fuser *Fuse) Readlink(path string) interface{} {
// 	//  fuse.readlink()
// 	full_path := Full_path(path)
// 	pathname, err := os.Readlink(full_path)
// 	if err != nil {
// 		if os.IsNotExist(err) {
// 			return OSError(2, "No such file or directory", path)
// 		}
// 		return OSError(1, err.Error(), path)
// 	}
// 	// if pathname.startswith("/"):
// 	// 	return os.path.relpath(pathname, FUSE.ROOT)
// 	// else:
// 	// 	return pathname
// 	return pathname
// }

// func (fuser *Fuse) Mknod(path string, mode int, dev int) interface{} {
// 	//  fuse.mknod()
// 	ret := syscall.Mknod(Full_path(path), uint32(mode), dev)
// 	return ret
// }

// func (fuser *Fuse) Rmdir(path string) interface{} {
// 	//  fuse.rmdir()
// 	full_path := Full_path(path)
// 	err := os.Remove(full_path)
// 	if err != nil {
// 		if os.IsNotExist(err) {
// 			// return OSError(2, "No such file or directory", path)
// 			// 不存在代表删除成功了
// 			return true
// 		}
// 		return OSError(1, err.Error(), path)
// 	}
// 	return true
// }

// func (fuser *Fuse) Mkdir(path string, mode int) interface{} {
// 	//  fuse.mkdir()
// 	err := os.Mkdir(Full_path(path), fs.FileMode(mode))
// 	if err != nil {
// 		if os.IsNotExist(err) {
// 			return OSError(2, "No such file or directory", path)
// 		}
// 		return OSError(1, err.Error(), path)
// 	}
// 	return true
// }

// func (fuser *Fuse) Statfs(path string) interface{} {
// 	//  fuse.statfs()
// 	full_path := Full_path(path)
// 	stv := syscall.Statfs_t{}
// 	err := syscall.Statfs(full_path, &stv)
// 	if err != nil {
// 		if os.IsNotExist(err) {
// 			return OSError(2, "No such file or directory", path)
// 		}
// 		return OSError(1, err.Error(), path)
// 	}
// 	// log.Printf("%v %+v", full_path, stv)
// 	return dict{
// 		"f_bavail":  stv.Bavail,
// 		"f_bfree":   stv.Bfree,
// 		"f_blocks":  stv.Blocks,
// 		"f_bsize":   stv.Bsize,
// 		"f_favail":  stv.Ffree,
// 		"f_ffree":   stv.Ffree,
// 		"f_files":   stv.Files,
// 		"f_flag":    stv.Flags,
// 		"f_frsize":  stv.Frsize,
// 		"f_namemax": stv.Namelen,
// 	}
// }

// func (fuser *Fuse) Unlink(path string) interface{} {
// 	return fuser.Rmdir(path)
// }

// func (fuser *Fuse) Symlink(name string, target string) interface{} {
// 	//  fuse.symlink()
// 	err := os.Symlink(target, name)
// 	if err != nil {
// 		return OSError(1, err.Error(), name)
// 	}
// 	return true
// }

// func (fuser *Fuse) Rename(old string, new string) interface{} {
// 	//  fuse.rename()
// 	err := os.Rename(Full_path(old), Full_path(new))
// 	if err != nil {
// 		return OSError(1, err.Error(), new)
// 	}
// 	return true
// }

// func (fuser *Fuse) Link(target string, name string) interface{} {
// 	//  fuse.link()
// 	err := os.Link(Full_path(target), Full_path(name))
// 	if err != nil {
// 		return OSError(1, err.Error(), name)
// 	}
// 	return true
// }

// func (fuser *Fuse) Utimens(path string, timestamps []float64) interface{} {
// 	//  fuse.utimens()
// 	var times []time.Time
// 	for _, ts := range timestamps {
// 		times = append(times, time.Unix(int64(ts), 0))
// 	}
// 	err := os.Chtimes(Full_path(path), times[0], times[1])
// 	if err != nil {
// 		return OSError(1, err.Error(), path)
// 	}
// 	return true
// }

func (fuser *Fuse) Open(path string, flag int) interface{} {
	//  fuse.open()
	full_path := Full_path(path)

	// mode := os.O_RDONLY

	// if (flags & LINUX_FNOCTTY) == LINUX_FNOCTTY {
	// 	mode |= syscall.O_NOCTTY
	// }
	// if FileExists(full_path) {
	// 	mode |= os.O_RDWR
	// } else {
	// 	mode |= os.O_CREATE
	// }

	file, err := os.OpenFile(full_path, flag, 0755) // syscall.O_RDONLY | syscall.O_NOCTTY | syscall.O_NONBLOCKS_IRUSR | syscall.S_IWUSR | syscall.S_IWRITE
	if err != nil {
		return OSError(1, err.Error(), path)
	}
	fd := int(file.Fd())
	logger.Printf("Open %v mode=0x%X fd=%v", path, flag, fd)
	fuser.Fds[fd] = file
	return fd
}

func (fuser *Fuse) Create(path string, mode int, fi int) interface{} {
	logger.Printf("Create %v mode=0x%X fi=%v", path, mode, fi)
	full_path := Full_path(path)
	file, err := os.OpenFile(full_path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777) // Create(full_path)
	if err != nil {
		return OSError(1, err.Error(), path)
	}
	fd := int(file.Fd())
	fuser.Fds[fd] = file
	logger.Printf("Open %v mode=0x%X fd=%v", path, mode, fd)
	return fd
}

func (fuser *Fuse) Read(path string, length int, offset int, fd int) interface{} {
	buffer := make([]byte, length)
	if _, ok := fuser.Fds[fd]; !ok {
		return OSError(1, "file not open", path)
	}
	file := fuser.Fds[fd]
	_, err := file.Seek(int64(offset), io.SeekStart)
	if err != nil {
		return OSError(1, err.Error(), path)
	}
	_, err = file.Read(buffer)
	if err != nil && err != io.EOF {
		return OSError(1, err.Error(), path)
	}
	return base64.StdEncoding.EncodeToString(buffer)
}

func (fuser *Fuse) Write(path string, buf string, offset int, fd int) interface{} {
	if _, ok := fuser.Fds[fd]; !ok {
		return OSError(1, "file not open", path)
	}
	file := fuser.Fds[fd]
	if _, err := file.Seek(int64(offset), io.SeekStart); err != nil {
		return OSError(1, err.Error(), path)
	}
	decodedBytes, _ := base64.StdEncoding.DecodeString(buf)
	count, err := file.Write(decodedBytes)
	if err != nil {
		return OSError(13, err.Error(), path)
	}
	return count
}

//	func (fuser *Fuse) Truncate(path string, length int, fd int) interface{} {
//		//  fuse.truncate()
//		full_path := Full_path(path)
//		err := syscall.Truncate(full_path, int64(length))
//		if err != nil {
//			return OSError(1, err.Error(), path)
//		}
//		return true
//	}
func (fuser *Fuse) Flush(path string, fd int) interface{} {
	if _, ok := fuser.Fds[fd]; !ok {
		return OSError(1, "file not open", path)
	}
	file := fuser.Fds[fd]
	if err := file.Sync(); err != nil {
		return true // OSError(1, err.Error(), path)
	}
	return true
}

func (fuser *Fuse) Release(path string, fd int) interface{} {
	//  fuse.release()
	// _ = syscall.Close(syscall.Handle(fd))
	if _, ok := fuser.Fds[fd]; !ok {
		return OSError(1, "file not open", path)
	}
	file := fuser.Fds[fd]
	defer func() {
		delete(fuser.Fds, fd)
	}()
	file.Close()
	return true
}

func (fuser *Fuse) Fsync(path string, fdatasync int, fd int) interface{} {
	//  fuse.fsync()
	return fuser.Flush(path, fd)
}

// func main_test() {
// 	fuser := &Fuse{}
// 	// fuser.Access("fuse.go", 0)
// 	// fuser.Chmod("fuse.go", 0644)
// 	// fuser.Chown("fuse.go", 197108, 197108)
// 	attr := fuser.Getattr("/tmp/", 0)
// 	log.Printf("attr: %+v", attr)

// 	links := fuser.Readlink("/etc/os-release")
// 	log.Printf("attr: %+v", links)

// 	_ = fuser.Mkdir("/tmp/test/", 0777)
// 	// defer Rmdir("/tmp/test/")

// 	root := fuser.Statfs("/tmp")
// 	root, _ = json.Marshal(root)
// 	log.Printf("root: %s", root)

// 	_ = fuser.Symlink("/tmp/test/test1", "/tmp/wick_bash_autocomplete.sh")
// 	_ = fuser.Link("/tmp/wick_bash_autocomplete.sh", "/tmp/test/hardlink")
// 	// defer Unlink("/tmp/test/hardlink")

// 	_ = fuser.Rename("/tmp/test/test1", "/tmp/test/test")
// 	// defer Unlink("/tmp/test/test")
// 	dirs := fuser.Readdir("/tmp/test/", 0)
// 	log.Printf("/tmp/test: %+v", dirs)

// 	_ = fuser.Utimens("/tmp/ttyd.x86_64-1.7.4", []float64{1701849604.8191702, 1701849604.8191702})
// 	fd := fuser.Open("/tmp/test/test", 2).(int)
// 	defer fuser.Release("/tmp/test/test", fd)

// 	txt := fuser.Read("", 200, 0, fd).(string)
// 	decodedBytes, _ := base64.StdEncoding.DecodeString(txt)
// 	log.Printf("Read: %s", decodedBytes)
// 	fuser.Write("/tmp/test/test", txt, 50, fd)
// 	fuser.Flush("/tmp/test/test", fd)
// 	// Truncate("/tmp/test/test", 250, fd)

// }
